/**
 * 
 */
package com.tim.apps.mockgps.model;

import static org.junit.Assert.*;

import org.junit.Before;
import org.junit.Test;

/**
 * @author schickin
 *
 */
public class TestGPSPosition {
	
	private GPSPosition greenwichPos;
	private GPSPosition lookingEast;
	private GPSPosition lookingNorthEast;
	private GPSPosition lookingNorthWest;
	private GPSPosition lookingWest;
	private GPSPosition lookingSouth;

	private GPSPosition eastOfGreenwichPos;
	private GPSPosition northPolePos;
	private static final double greenwichLat = 51.5;
	
	@Before
	public final void setUp()
	{
		greenwichPos = new GPSPosition(greenwichLat, 0, 0, 0, 0, 0);
		lookingEast = greenwichWithBearing(90.0);
		lookingNorthEast = greenwichWithBearing(45.0);
		lookingNorthWest = greenwichWithBearing(315.0);
		lookingWest = greenwichWithBearing(270.0);
		lookingSouth = greenwichWithBearing(180.0);
		eastOfGreenwichPos = new GPSPosition(greenwichLat, 90, 0, 0, 0, 0);
		northPolePos = new GPSPosition(0, 0, 0, 0, 0, 0);
	}
	
	private GPSPosition greenwichWithBearing(double bearing)
	{
		return greenwichPos.clone().setBearing(bearing);
	}
	
	/**
	 * Test method for {@link com.tim.apps.mockgps.model.GPSPosition#bearing(GPSPosition, GPSPosition)}.
	 */
	@Test
	public final void testBearingFirstQuadrant() {
		assertEquals(-Math.PI/2.0, Math.atan2(-90.0, 0.0),.001);
		assertEquals(Math.PI, Math.atan2(0, -90.0),.001);
		assertEquals(Math.PI/2.0, Math.atan2(90.0, 0.0),.001);
		assertEquals(0.0, Math.atan2(0, 90.0),.001);
		assertEquals(270.0, GPSPosition.bearing(eastOfGreenwichPos, greenwichPos), .0001);
		assertEquals(90.0, GPSPosition.bearing(greenwichPos, eastOfGreenwichPos), .0001);
	}
	
	/**
	 * Test method for {@link com.tim.apps.mockgps.model.GPSPosition#bearing(GPSPosition, GPSPosition)}.
	 */
	@Test
	public final void testBearingFourthQuadrant() {
		GPSPosition westOfGreenwichPos = greenwichPos.clone().setLng(-90.0);
		assertEquals(90.0, GPSPosition.bearing(westOfGreenwichPos, greenwichPos), .0001);
		assertEquals(270.0, GPSPosition.bearing(greenwichPos, westOfGreenwichPos), .0001);
	}

	/**
	 * Test method for {@link com.tim.apps.mockgps.model.GPSPosition#interpolate(com.tim.apps.mockgps.model.GPSPosition, com.tim.apps.mockgps.model.GPSPosition, double)}.
	 */
	@Test
	public final void testMoveNorth() {
		GPSPosition halfwayNorth = new GPSPosition(greenwichLat/2.0, 0, 0, 0, 0, 0); 
		assertInterpolationFromMiddleTo(greenwichPos, halfwayNorth, northPolePos);
	}
	
	/**
	 * Test method for {@link com.tim.apps.mockgps.model.GPSPosition#interpolate(com.tim.apps.mockgps.model.GPSPosition, com.tim.apps.mockgps.model.GPSPosition, double)}.
	 */
	@Test
	public final void testMoveEast() {
		GPSPosition oneQuarterEast = new GPSPosition(greenwichLat, eastOfGreenwichPos.getLng()/4.0, 0, 0, 0, 0);
		GPSPosition threeQuartersEast = new GPSPosition(greenwichLat, eastOfGreenwichPos.getLng()*0.75, 0, 0, 0, 0);
		assertInterpolationFromTwoStepsTo(greenwichPos, oneQuarterEast, threeQuartersEast, eastOfGreenwichPos);
	}

	/**
	 * Test method for {@link com.tim.apps.mockgps.model.GPSPosition#interpolate(com.tim.apps.mockgps.model.GPSPosition, com.tim.apps.mockgps.model.GPSPosition, double)}.
	 */
	@Test
	public final void testTurnEasternQuadrant() {
		assertInterpolationFromMiddleTo(greenwichPos, lookingNorthEast, lookingEast);
		assertInterpolationFromMiddleTo(lookingEast, lookingNorthEast, greenwichPos);
	}
	
	/**
	 * Test method for {@link com.tim.apps.mockgps.model.GPSPosition#interpolate(com.tim.apps.mockgps.model.GPSPosition, com.tim.apps.mockgps.model.GPSPosition, double)}.
	 */
	@Test
	public final void testTurnWesternQuadrant() {
		assertInterpolationFromMiddleTo(greenwichPos, lookingNorthWest, lookingWest);
	}
	
	/**
	 * Test method for {@link com.tim.apps.mockgps.model.GPSPosition#interpolate(com.tim.apps.mockgps.model.GPSPosition, com.tim.apps.mockgps.model.GPSPosition, double)}.
	 */
	@Test
	public final void testTurnOverNorth() {
		assertInterpolationFromMiddleTo(lookingNorthWest, greenwichPos, lookingNorthEast);
		assertInterpolationFromMiddleTo(lookingNorthEast, greenwichPos, lookingNorthWest);
	}
	
	/**
	 * Test method for {@link com.tim.apps.mockgps.model.GPSPosition#interpolate(com.tim.apps.mockgps.model.GPSPosition, com.tim.apps.mockgps.model.GPSPosition, double)}.
	 */
	@Test
	public final void testTurnLargeAngle() {
		assertInterpolationFromMiddleTo(lookingSouth, greenwichWithBearing(45.0 + 135.0/2.0), lookingNorthEast);
		assertInterpolationFromMiddleTo(lookingSouth, greenwichWithBearing(315.0 - 135.0/2.0) , lookingNorthWest);
	}
	
	private final void assertInterpolationFromMiddleTo(
			GPSPosition from, GPSPosition middle, GPSPosition to) {
		assertEqualsPosition(from,
				GPSPosition.interpolate(from, to, 0));
		assertEqualsPosition(middle,
				GPSPosition.interpolate(from, to, 0.5));
		assertEqualsPosition(to,
				GPSPosition.interpolate(from, to, 1));
		
	}
	
	private final void assertInterpolationFromTwoStepsTo(
			GPSPosition from, GPSPosition oneQuarter,
			GPSPosition threeQuarters, GPSPosition to) {
		assertEqualsPosition(from,
				GPSPosition.interpolate(from, to, 0));
		assertEqualsPosition(oneQuarter,
				GPSPosition.interpolate(from, to, 0.25));
		assertEqualsPosition(threeQuarters,
				GPSPosition.interpolate(from, to, 0.75));
		assertEqualsPosition(to,
				GPSPosition.interpolate(from, to, 1));
		
	}
	
	private final void assertEqualsPosition(GPSPosition expected, GPSPosition actual)
	{
		assertEquals(expected.toString(), actual.toString());
	}

}
